package lily.com.app;

import android.graphics.Point;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class GestureListener implements View.OnTouchListener {

	public interface GestureNone {
		int NONE = 0x0;
	}

	public interface GestureSinglePoint {
		int NONE = 0x10;
		int CLICK = 0x11;
		int DRAG = 0x20;
		int SWIP = 0x30;
		int SWIP_RIGTH = 0x31;
		int SWIP_LEFT = 0x32;
		int SWIP_UP = 0x33;
		int SWIP_DOWN = 0x34;
	}

	public interface GestureMultiPoint {
		int NONE = 0x40;
		int ZOOM = 0x50;
		int ZOOMIN = 0x51;
		int ZOOMOUT = 0x52;
	}

	public static float ZOOM_THRESHOLD = 40f;
	public static float CLICK_THRESHOLD = 30f;
	public static int DRAG_CNT_THRESHOLD = 10;

	Point fromPoint = new Point();
	Point toPoint = new Point();
	Point[] fromPoints = new Point[]{new Point(), new Point()};
	Point[] toPoints = new Point[]{new Point(), new Point()};

	float downDist = 0f;
	float moveDist = 0f;
	
	int mode = GestureNone.NONE;
	int catagory = GestureNone.NONE;
	int moveStepCnt = 0;

	private GestureResponder gestureResponder = null;

	public GestureListener() {}

	public void setGestureResponder(GestureResponder gestureResponder) {
		this.gestureResponder = gestureResponder;
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		
		if(gestureResponder == null) return true;
		
		// dumpEvent(event);
		
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
//			Log.d("DEBUG", "GestureListener ACTION_DOWN");
			catagory = GestureSinglePoint.NONE;
			mode =  GestureSinglePoint.NONE;
			moveStepCnt = 0;
			fromPoint.set((int)event.getX(), (int)event.getY());
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
//			Log.d("DEBUG", "GestureListener ACTION_POINTER_DOWN");
			catagory = GestureMultiPoint.NONE;
			mode = GestureMultiPoint.NONE;
			moveStepCnt = 0;
			fromPoints[0].set((int)event.getX(0), (int)event.getY(0));
			fromPoints[1].set((int)event.getX(1), (int)event.getY(1));
//			Log.d("DEBUG", "x1 = " + event.getX(0));
//			Log.d("DEBUG", "y1 = " + event.getY(0));
//			Log.d("DEBUG", "x2 = " + event.getX(1));
//			Log.d("DEBUG", "y2 = " + event.getY(1));
			downDist = spacing(event);
			break;
		case MotionEvent.ACTION_UP:
//			Log.d("DEBUG", "GestureListener ACTION_UP");
			toPoint.set((int)event.getX(), (int)event.getY());
			if (catagory == GestureSinglePoint.NONE) {
				gestureResponder.onClick(v, toPoint);
			}
			if(catagory == GestureSinglePoint.DRAG){
				gestureResponder.stopDrag(v, fromPoint, toPoint);
			}
			if(catagory == GestureSinglePoint.SWIP){
				return true;
			}
			catagory = GestureNone.NONE;
			mode = GestureNone.NONE;
			moveStepCnt = 0;
			break;
		case MotionEvent.ACTION_POINTER_UP:
//			Log.d("DEBUG", "GestureListener ACTION_POINTER_UP");
			gestureResponder.stopDrag(v, fromPoint, toPoint);
			catagory = GestureNone.NONE;
			mode = GestureNone.NONE;
			moveStepCnt = 0;
			break;
		case MotionEvent.ACTION_MOVE:
			//TODO bad smell, use swith instead of if
//			Log.d("DEBUG", "GestureListener ACTION_MOVE");
//			Log.d("DEBUG", "x = " + event.getX());
//			Log.d("DEBUG", "y = " + event.getY());
			moveStepCnt++;
			toPoint.set((int)event.getX(), (int)event.getY());
			if (catagory == GestureSinglePoint.NONE) {
				if (moveStepCnt < DRAG_CNT_THRESHOLD) {
					moveDist = spacing(fromPoint, toPoint);
//					Log.d("DEBUG","moveDist="+ moveDist);
					if(moveDist > CLICK_THRESHOLD){
						catagory = GestureSinglePoint.SWIP;
						startSwipe(v, fromPoint, toPoint);
					}
				}
				if (moveStepCnt == DRAG_CNT_THRESHOLD) {
					catagory = GestureSinglePoint.DRAG;
					gestureResponder.startDrag(v, fromPoint, toPoint);
				}
				break;
			}
			if(catagory == GestureSinglePoint.DRAG){
				gestureResponder.onDrag(v, fromPoint, toPoint);
				fromPoint.set(toPoint.x, toPoint.y);
				break;
			}
			if(catagory == GestureSinglePoint.SWIP){
				onSwipe(v, fromPoint, toPoint);
				fromPoint.set(toPoint.x, toPoint.y);
				break;
			}
			if(catagory == GestureMultiPoint.NONE){
				moveDist = spacing(event);
				if(moveDist > ZOOM_THRESHOLD){
					float diff = moveDist - downDist;
//					Log.d("DEBUG", "moveDist = " + moveDist);
//					Log.d("DEBUG", "downDist = " + downDist);
					toPoints[0].set((int)event.getX(0), (int)event.getY(0));
					toPoints[1].set((int)event.getX(1), (int)event.getY(1));
					if (diff > ZOOM_THRESHOLD) {
						mode = GestureMultiPoint.ZOOMIN;
						catagory = GestureMultiPoint.ZOOM;
						gestureResponder.startZoomIn(v, fromPoints, toPoints);
					} else if (diff < -ZOOM_THRESHOLD) {
						mode = GestureMultiPoint.ZOOMOUT;
						catagory = GestureMultiPoint.ZOOM;
						gestureResponder.startZoomOut(v, fromPoints, toPoints);
					}
					fromPoints[0].x = toPoints[0].x;
					fromPoints[0].y = toPoints[0].y;
					fromPoints[1].x = toPoints[1].x;
					fromPoints[1].y = toPoints[1].y;
					downDist = moveDist;
				}
				break;
			}
			if(catagory == GestureMultiPoint.ZOOM){
				moveDist = spacing(event);
				if(moveDist > ZOOM_THRESHOLD){
					float diff = moveDist - downDist;
//					Log.d("DEBUG", "moveDist = " + moveDist);
//					Log.d("DEBUG", "downDist = " + downDist);
					toPoints[0].set((int)event.getX(0), (int)event.getY(0));
					toPoints[1].set((int)event.getX(1), (int)event.getY(1));
					if (diff > ZOOM_THRESHOLD) {
						mode = GestureMultiPoint.ZOOMIN;
						catagory = GestureMultiPoint.ZOOM;
						gestureResponder.onZoomIn(v, fromPoints, toPoints);
					} else if (diff < -ZOOM_THRESHOLD) {
						mode = GestureMultiPoint.ZOOMOUT;
						catagory = GestureMultiPoint.ZOOM;
						gestureResponder.onZoomOut(v, fromPoints, toPoints);
					}
					fromPoints[0].x = toPoints[0].x;
					fromPoints[0].y = toPoints[0].y;
					fromPoints[1].x = toPoints[1].x;
					fromPoints[1].y = toPoints[1].y;
					downDist = moveDist;
				}
				break;
			}
			break;
		default:
			break;
		}
		return false;
	}

	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	private float spacing(Point fromPoint, Point toPoint) {
//		Log.d("DEBUG", "fromPoint.x = " + fromPoint.x);
//		Log.d("DEBUG", "fromPoint.y = " + fromPoint.y);
//		Log.d("DEBUG", "toPoint.x = " + toPoint.x);
//		Log.d("DEBUG", "toPoint.y = " + toPoint.y);
		float x = fromPoint.x - toPoint.x;
		float y = fromPoint.y - toPoint.y;
		return FloatMath.sqrt(x * x + y * y);
	}

	private void onSwipe(View v, Point fromPoint, Point toPoint) {

		float disX = fromPoint.x - toPoint.x;
		float disY = fromPoint.y - toPoint.y;
		
		if(disX < 0) disX = -disX;
		if(disY < 0) disY = -disY;

		if (disX > disY) {
			if (toPoint.x > fromPoint.x)
				gestureResponder.onSwipeRight(v, fromPoint, toPoint);
			else
				gestureResponder.onSwipeLeft(v, fromPoint, toPoint);
		} else {
			if (toPoint.y > fromPoint.y)
				gestureResponder.onSwipeDown(v, fromPoint, toPoint);
			else
				gestureResponder.onSwipeUp(v, fromPoint, toPoint);
		}
	}
	
	private void startSwipe(View v, Point fromPoint, Point toPoint) {

		float disX = fromPoint.x - toPoint.x;
		float disY = fromPoint.y - toPoint.y;
		
		if(disX < 0) disX = -disX;
		if(disY < 0) disY = -disY;

		if (disX > disY) {
			if (toPoint.x > fromPoint.x)
				gestureResponder.startSwipeRight(v, fromPoint, toPoint);
			else
				gestureResponder.startSwipeLeft(v, fromPoint, toPoint);
		} else {
			if (toPoint.y > fromPoint.y)
				gestureResponder.startSwipeDown(v, fromPoint, toPoint);
			else
				gestureResponder.startSwipeUp(v, fromPoint, toPoint);
		}
	}
}
